unless defined?(RediPress)
  raise "You can't load this file. Please load the redipress file instead."
end

require 'sshkit'
require 'sshkit/dsl'

module RediPress
  # This module contains all configurations
  #
  module Configuration
    # Returns a Hash of all available configurations.
    #
    # Example:
    #   >> RediPress::Configuration.all
    #   => { "test" => #<RediPress::Configuration::Test:0x00000000000000> }
    #
    def self.all
      instances_of(*slugs)
    end

    # Returns a Hash containing instances of the specified configurations.
    #
    # Arguments:
    #   slugs: (Splat)
    #
    # Example:
    #   >> RediPress::Configuration.instances_of :test
    #   => { "test" => #<RediPress::Configuration::Test:0x00000000000000> }
    #
    def self.instances_of(*slugs)
      slugs.sort_by! { |slug| slug }
      slugs.map! { |slug| [slug.to_s, instance_of(slug.to_s)] }

      Hash[*slugs.flatten]
    end

    # Returns an instance of the specified configuration.
    #
    # Arguments:
    #   slug: (Symbol|String)
    #
    # Example:
    #   >> RediPress::Configuration.instance_of :test
    #   => #<RediPress::Configuration::Test:0x00000000000000>
    #
    def self.instance_of(slug)
      load_by_slugs(slug)

      klass = class_for(slug)
      klass = Object.const_get(klass)
      klass.new
    end

    # Get the class name for a configuration.
    #
    # Arguments:
    #   slug: (Symbol|String)
    #
    # Example:
    #   >> RediPress::Configuration.class_for :test
    #   => "RediPress::Configuration::Test"
    #
    def self.class_for(slug)
      klass = slug.to_s.capitalize
      klass.gsub!(/[-_.\s]([a-zA-Z0-9])/) { $1.upcase }
      klass.tr!("+", "x")
      "RediPress::Configuration::#{klass}"
    end

    # Get the slug for a configuration class.
    #
    # Arguments:
    #   klass: (Class)
    #
    # Example:
    #   >> RediPress::Configuration.slug_for RediPress::Configuration::Test
    #   => "test"
    #
    def self.slug_for(klass)
      slug = klass.to_s.split("::").last
      slug.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
      slug.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
      slug.tr!("-", "_")
      slug.downcase!
      slug
    end

    # Load a configuration from the configurations directory.
    #
    # Arguments:
    #   slugs: (Splat)
    #
    # Example:
    #   >> RediPress::Configuration.load_by_slugs :test
    #   => nil
    #
    def self.load_by_slugs(*slugs)
      ensure_available!(*slugs)

      slugs.each do |slug|
        require gems[slug.to_s]
      end

      nil
    end

    # Ensure that the specified configurations are available.
    #
    # Arguments:
    #   slugs: (Splat)
    #
    # Example:
    #   >> RediPress::Configuration.ensure_available! :test
    #   => nil
    #
    # Raises:
    #   RediPress::ConfigurationNotAvailable: If one or more of the specified configurations are not available.
    #
    def self.ensure_available!(*slugs)
      slugs.each do |slug|
        raise RediPress::ConfigurationNotAvailable.new(slug) unless available?(slug)
      end

      nil
    end

    # Check that the specified configurations are available.
    #
    # Arguments:
    #   slugs: (Splat)
    #
    # Example:
    #   >> RediPress::Configuration.available? :test
    #   => true
    #
    def self.available?(*slugs)
      slugs.each do |slug|
        return false unless self.slugs.include?(slug.to_s)
      end

      true
    end

    # Get the slugs of all configurations
    #
    # Example:
    #   >> RediPress::Configuration.slugs
    #   => ["test"]
    #
    def self.slugs
      gems.keys
    end

    # Get the all gems related to configurations
    #
    # Example:
    #   >> RediPress::Configuration.gems
    #   => { "test" => "redipress/configuration/test" }
    #
    def self.gems
      all_gems = Gem::Specification.select do |gem|
        gem.name.downcase =~ /^redipress-configuration-[a-z0-9-]+$/
      end

      gems = Hash.new

      all_gems.each do |gem|
        slug = gem.name.downcase.sub("redipress-configuration-", "").gsub("-", "_")

        gems[slug] = "redipress/configuration/#{slug}"
      end

      gems
    end

    # Set the paths to the private SSH Keys to use
    #
    # Arguments:
    #   keys: (Splat)
    #
    # Example:
    #   >> RediPress::Configuration.set_ssh_key_paths('/home/rediwebhosting/.ssh/id_rsa')
    #   => nil
    #
    def self.set_ssh_key_paths(*keys)
      SSHKit::Backend::Netssh.configure do |ssh|
        ssh.connection_timeout = 60
        ssh.ssh_options = {
          keys: keys,
          forward_agent: false,
          auth_methods: %w(publickey password)
        }
      end
    end

    # Mark certain class methods as private
  	private_class_method :load_by_slugs
  end
end
